package storage

// Redis 相关操作指令
// Redis 涉及到的数据结构有  String List Set Hash SortedSet

import (
	"fmt"
	"time"

	"github.com/garyburd/redigo/redis"
)

var redisPool *RedisPool

type RedisPool struct {
	RedisClient *redis.Pool
}

const (
	redisMaxIdle          = 3
	redisIdleTimeoutSec   = 240
	redisDialReadTimeout  = 60 * time.Second
	redisDialWriteTimeout = 10 * time.Second
	redisPassword         = "yyets@redis"
)

var RedisConn *redis.Pool

// NewRedisPool returns a new Redis connection pool.
func InitRedisPool(redisURL string) error {
	RedisConn = &redis.Pool{
		MaxIdle:     redisMaxIdle,
		IdleTimeout: redisIdleTimeoutSec * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", redisURL,
				redis.DialReadTimeout(redisDialReadTimeout),
				redis.DialWriteTimeout(redisDialWriteTimeout),
				redis.DialPassword(redisPassword),
			)
			if err != nil {
				return nil, fmt.Errorf("redis connection error: %s", err)
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			if err != nil {
				return fmt.Errorf("ping redis error: %s", err)
			}
			return nil
		},
	}
	return nil
}

/**
+----------------------------------------------------------
* 通用操作
+----------------------------------------------------------
* @access public
+----------------------------------------------------------
* @function 	RedisExpire   设置过期时间
* @function 	RedisExists   检查key是否存在
* @function 	RedisDelete   删除key对应是数据
+----------------------------------------------------------
*/

// 设置过期时间
func RedisExpire(k string, time int) (bool, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Bool(c.Do("EXPIRE", k, time))
	if err != nil {
		return false, err
	}
	return reply, nil
}

// 检查key是否存在
func RedisExists(k string) bool {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Bool(c.Do("EXISTS", k))
	if err != nil {
		return false
	}
	return reply
}

// 删除key对应是数据
func RedisDelete(k string) (reply interface{}, err error) {
	c := RedisConn.Get()
	defer c.Close()
	return c.Do("DEL", k)
}



/**
+----------------------------------------------------------
* 字符串 String
+----------------------------------------------------------
* @access public
+----------------------------------------------------------
* @function 	RedisSet   		将字符串值 value 关联到 key
* @function 	RedisGet   		返回 key 所关联的字符串值
* @function 	RedisGetList   	返回 key 所关联的字符串值为 字符串数组
* @function 	RedisIncr   	string类型缓存自增1
+----------------------------------------------------------
*/

// 将字符串值 value 关联到 key
func RedisSet(k string, value interface{}, time int) (bool, error) {
	c := RedisConn.Get()
	defer c.Close()
	if time >= 0 {
		str, err := redis.String(c.Do("SETEX", k, time, value))
		if err != nil && str != "OK" {
			return false, err
		}
	}else {
		str, err := redis.String(c.Do("SET", k, value))
		if str != "OK" && err != nil {
			return false, err
		}
	}
	return  true, nil
}

// 返回 key 所关联的字符串值
func RedisGet(k string) (string, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.String(c.Do("GET", k))
	if err != nil {
		return "", err
	}
	return reply, nil
}

// 返回 key 所关联的字符串值为 字符串数组
func RedisGetList(k string) ([]byte, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Bytes(c.Do("GET", k))
	if err != nil {
		return nil, err
	}
	return reply, nil
}

// string类型缓存自增1
func RedisIncr(k string) (int, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Int(c.Do("INCR", k))
	if err != nil {
		return 0, err
	}
	return reply, nil
}


/**
+----------------------------------------------------------
* 列表 List
+----------------------------------------------------------
* @access public
+----------------------------------------------------------
* @function 	RedisLPush   命令将一个值插入到列表头部
* @function 	RedisRPush   在列表中添加一个值
* @function 	RedisLPop    移除并返回列表的第一个元素
* @function 	RedisLLen    获取列表的长度
* @function 	RedisLRem    移除列表中与参数 VALUE 相等的元素
+----------------------------------------------------------
*/

// 命令将一个值插入到列表头部
func RedisLPush(k string, value interface{}) (int, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Int(c.Do("LPUSH", k, value))
	if err != nil {
		return 0, err
	}
	return reply, nil
}

// 在列表中添加一个值
func RedisRPush(k string, value interface{}) (int, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Int(c.Do("RPUSH", k, value))
	if err != nil {
		return 0, err
	}
	return reply, nil
}

// 移除并返回列表的第一个元素
func RedisLPop(k string) (string, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.String(c.Do("LPOP", k))
	if err != nil {
		return "", err
	}
	return reply, nil
}

// 获取列表的长度
func RedisLLen(k string) (int, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Int(c.Do("LLEN", k))
	if err != nil {
		return 0, err
	}
	return reply, nil
}

// 移除列表中与参数 VALUE 相等的元素
func RedisLRem(k string, count int, value interface{}) (int, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Int(c.Do("LREM", k, count, value))
	if err != nil {
		return 0, err
	}
	return reply, nil
}



/**
+----------------------------------------------------------
* 集合 set
+----------------------------------------------------------
* @access public
+----------------------------------------------------------
* @function 	RedisIsMember   	判断 member 元素是否集合 key 的成员
* @function 	RedisSAdd   		在列表中添加一个值
* @function 	RedisSMembers    	返回集合 key 中的所有成员
* @function 	RedisSRem	    	移除集合 key 中所有成员
+----------------------------------------------------------
*/

//	如果 member 元素是集合的成员，返回 1
//	如果 member 元素不是集合的成员，或 key 不存在，返回 0
func RedisIsMember(k, member string) bool {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Int(c.Do("SISMEMBER", k, member))
	if err == nil && reply == 1 {
		return true
	}
	return false
}

// 在列表中添加一个值
func RedisSAdd(k, v string) (bool, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Bool(c.Do("SADD", k, v))
	if err != nil {
		return false, err
	}
	return reply, nil
}

// 返回集合 key 中的所有成员
func RedisSMembers(k string) ([]interface{}, bool) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Values(c.Do("SMEMBERS", k))
	if err != nil {
		return nil, false
	}
	return reply, true
}

// 移除集合 key 中的一个或多个 member 元素，不存在的 member 元素会被忽略。
// 当 key 不是集合类型，返回一个错误。
func RedisSRem(k, v string) (bool, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Bool(c.Do("SREM", k, v))
	if err == nil || reply == true {
		return true, nil
	}
	return  false, err
}

/**
+----------------------------------------------------------
* 哈希 Hash
+----------------------------------------------------------
* @access public
+----------------------------------------------------------
* @function 	RedisHSet   	添加元素到集合
* @function 	RedisHGetAll   	以列表形式返回哈希表的域和域的值  若 key 不存在，返回空列表
* @function 	RedisHGet    	获取集合中的元素
+----------------------------------------------------------
*/

// 获取集合中的元素
func RedisHExists(k, field string) (bool, error) {
	c := RedisConn.Get()
	defer c.Close()
	result, err := redis.Int(c.Do("HEXISTS", k, field))
	if err != nil || result == 0 {
		return false, err
	}
	return true, nil
}


// 如果 field 是哈希表中的一个新建域，并且值设置成功，返回 1
// 如果哈希表中域 field 已经存在且旧值已被新值覆盖，返回 0
func RedisHSet(hash string, data map[string]interface{}, time int) (bool, error) {
	c := RedisConn.Get()
	defer c.Close()

	for key, value := range data {
		_, err := redis.Bool(c.Do("hset", hash, key, value))
		if err != nil {
			return false, err
		}
	}
	if time >= 0 {
		c.Do("EXPIRE", hash, time)
	}

	return true, nil
}
// 以列表形式返回哈希表的域和域的值  若 key 不存在，返回空列表。
func RedisHGetAll(hash string) (map[string]interface{}, error) {
	c := RedisConn.Get()
	defer c.Close()

	resultMap := make(map[string]interface{})
	result, err := redis.Values(c.Do("hgetall", hash))
	if err != nil {
		fmt.Println("hgetall failed", err.Error())
		return nil, err
	} else {

		for index, v := range result {
			if index%2 != 0 {
				keyIndex := index - 1
				stringV := string(v.([]byte))
				key := string(result[keyIndex].([]byte))
				resultMap[key] = stringV
			}
		}
	}

	return resultMap, nil
}
// 获取集合中的元素
func RedisHGet(k, field string) (string, error) {
	c := RedisConn.Get()
	defer c.Close()

	result, err := redis.String(c.Do("HGet", k, field))
	if err != nil {
		return "", err
	}
	return result, nil
}




/**
+----------------------------------------------------------
* 有序集合 SortedSet
+----------------------------------------------------------
* @access public
+----------------------------------------------------------
* @function 	RedisZAdd   	添加元素到有序集合
* @function 	RedisZRem   	移除有序集合中的元素
* @function 	RedisZCard    	查询有序集合的成员数
* @function 	RedisZRange    	通过索引区间返回有序集合成指定区间内的成员
* @function 	RedisZScore    	返回有序集 key 中，成员 member 的 score 值
+----------------------------------------------------------
*/

// 判断有序集中中是否有此成员
func RedisZIsMember(k, v string) (bool, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Int(c.Do("ZRANK", k, v))
	if err != nil || reply < 0 {
		return false, err
	}
	return true, nil
}

// 添加元素到有序集合
func RedisZAdd(k, v string, score int64) (int, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Int(c.Do("ZADD", k, score, v))
	if err != nil {
		return 0, err
	}
	return reply, nil
}

// 移除有序集合中的元素
func RedisZRem(k string, value string) (int, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Int(c.Do("ZREM", k, value))
	if err != nil {
		return 0, err
	}
	return reply, nil
}

// 查询有序集合的成员数
func RedisZCard(k string) (int, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Int(c.Do("ZCARD", k))
	if err != nil {
		return 0, err
	}
	return reply, nil
}

// 通过索引区间返回有序集合成指定区间内的成员
func RedisZRange(k string, start, stop int) ([]string, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Strings(c.Do("ZRANGE", k, start, stop))
	if err != nil {
		return reply, err
	}
	return reply, nil
}

// 返回有序集 key 中，成员 member 的 score 值
func RedisZScore(k, v string) (int, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Int(c.Do("ZSCORE", k, v))
	if err != nil {
		return 0, err
	}
	return reply, nil
}

// (倒序)通过索引区间返回有序集合成指定区间内的成员
func RedisZRevRange(k string, start, stop int) ([]string, error) {
	c := RedisConn.Get()
	defer c.Close()
	reply, err := redis.Strings(c.Do("ZREVRANGE", k, start, stop))
	if err != nil {
		return reply, err
	}
	return reply, nil
}

